﻿// -------------------------------------------------------------------------
//    @FileName         :    NFAttr.h
//    @Author           :    Gao.Yi
//    @Date             :    2025-09-30
//    @Email			:    445267987@qq.com
//    @Module           :    NFAttr
//    @Desc             :
//
// -------------------------------------------------------------------------

#pragma once

#include "E_Actor.nanopb.h"
#include "NFBaseDefine.h"
#include "NFComm/NFObjCommon/NFObject.h"
#include "NFComm/NFPluginModule/NFExprCheckMacro.h"
#include "NFComm/NFPluginModule/NFMathOp.h"
#include "NFCommMacros.h"
#include "proto_common_const.pb.h"

#define MAX_CD_RDU               (0.30f)
#define MAX_CD_RDU_PRO           (0.25f)
#define MAX_CD_RDU_FORCE_DODGE   (0.15f)
#define MAX_CD_RDU_PARTNER_SP    (0.20f)
#define MAX_CD_RDU_DODGE         (0.15f)
#define MAX_CD_RDU_BUFF_SKILL    (0.25f)

#define MAX_MOV_SPD_GAME         (0.40f)
#define MAX_MOV_SPD              (0.60f)


#define ADD_ATTR_VALUE_AND_CHECK_RET_NO_CHECK_RAW(rOwner, eType, value) \
    do \
    { \
        iRetCode = AddAttrValueNoCheckRaw((rOwner), (eType), (value)); \
        CHECK_RET(iRetCode, "add attr value error: {}", eType); \
    } while(0);

#define ADD_ATTR_VALUE_AND_CHECK_RET(rOwner, eType, value) \
    do \
    { \
        iRetCode = AddAttrValue((rOwner), (eType), (value)); \
        CHECK_RET(iRetCode, "add attr value error: {}", eType); \
    } while(0);

#define ADD_ATTR_AND_CHECK_RET(rOwner, value) \
    do \
    { \
        iRetCode = AddAttrValue((rOwner), (value)); \
        CHECK_RET(iRetCode, "add attr value error:{}", value.type); \
    } while(0);

#define ADD_HERO_CE_AND_CE_TYPE(rOwner, value, pCEData, eCEType) \
    do \
    { \
        ADD_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CE_NEW, value); \
        if(pCEData) \
        { \
            AddCE(pCEData, eCEType, value); \
        } \
    } while(0);

#define SET_ATTR_AND_CHECK_RET(rOwner, eType, value) \
    do \
    { \
        iRetCode = SetAttr((rOwner), (eType), (value)); \
        CHECK_RET(iRetCode, "set attr value error{}", eType); \
    } while(0);

#define SET_ATTR_VALUE_AND_CHECK_RET_NO_CHECK_RAW(rOwner, eType, value) \
    do \
    { \
        iRetCode = SetAttrValuenNoCheckRaw((rOwner), (eType), (value)); \
        CHECK_RET(iRetCode, "set attr value error{}", eType); \
    } while(0);

#define SET_ATTR_VALUE_AND_CHECK_RET(rOwner, eType, value) \
    do \
    { \
        iRetCode = SetAttrValue((rOwner), (eType), (value)); \
        CHECK_RET(iRetCode, "set attr value error{}", eType); \
    } while(0);

#define GET_ATTR_VALUE_AND_CHECK_RET(rOwner, eType, value) \
    do \
    { \
        iRetCode = GetAttrValue((rOwner), (eType), (value)); \
        CHECK_RET(iRetCode, "get attr value error{}", eType); \
    } while(0);

#define MULTI_ATTR_VALUE_AND_CHECK_RET(rOwner, eType, value) \
    do \
    { \
        iRetCode = MultiAttrValue((rOwner), (eType), (value)); \
        CHECK_RET(iRetCode, "multi attr value error{}", eType); \
    } while(0);

inline bool IS_BATTLE_ATTR(int eAttr)
{
    switch (eAttr)
    {
        case proto_ff::EN_ATTR_STR:
        case proto_ff::EN_ATTR_MAG:
        case proto_ff::EN_ATTR_VIT:
        case proto_ff::EN_ATTR_DEX:
        case proto_ff::EN_ATTR_LUCK:
        case proto_ff::EN_ATTR_SPR:
        case proto_ff::EN_ATTR_PHY_DMG:
        case proto_ff::EN_ATTR_MGC_DMG:
        case proto_ff::EN_ATTR_ARMOR:
        case proto_ff::EN_ATTR_RESIST:
        case proto_ff::EN_ATTR_CRI_RATE:
        case proto_ff::EN_ATTR_CRI_RATE_RDU:
        case proto_ff::EN_ATTR_CRI_DMG:
        case proto_ff::EN_ATTR_CRI_DMG_RDU:
        case proto_ff::EN_ATTR_MAX_HP:
        case proto_ff::EN_ATTR_MAX_MP:
        case proto_ff::EN_ATTR_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_REAL_DMG:
        case proto_ff::EN_ATTR_REAL_DMG_RDU:
        case proto_ff::EN_ATTR_PHY_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_MGC_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_A_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_B_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_C_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_D_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_E_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_F_DMG_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_A_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_B_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_C_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_D_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_E_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_PROF_F_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_PHY_DEF_AMPLIFY:
        case proto_ff::EN_ATTR_MGC_DEF_AMPLIFY:
            return true;
            break;
        default:
            return false;
            break;
    }

    return false;
}


//战斗力计算属性与对应系数
const proto_ff::AttrType arrCalcCEAttr[] = {
    proto_ff::EN_ATTR_MAX_HP, proto_ff::EN_ATTR_PHY_DMG, proto_ff::EN_ATTR_ARMOR, proto_ff::EN_ATTR_CRI_RATE, proto_ff::EN_ATTR_CRI_RATE_RDU, proto_ff::EN_ATTR_REAL_DMG, proto_ff::EN_ATTR_REAL_DMG_RDU, proto_ff::EN_ATTR_DMG_AMPLIFY, proto_ff::EN_ATTR_DEF_AMPLIFY, proto_ff::EN_ATTR_CRI_DMG,
    proto_ff::EN_ATTR_CRI_DMG_RDU, proto_ff::EN_ATTR_CD_RDU, proto_ff::EN_ATTR_ACT_SPD, proto_ff::EN_ATTR_MOV_SPD, proto_ff::EN_ATTR_RECOVER_SP, proto_ff::EN_ATTR_HP_RECOVERY,
};

const float arrCalcCERatio[] = {0.08f, 1.0f, 1.0f, 2.0f, 2.0f, 4.0f, 4.0f, 400, 400, 400, 400, 400, 400, 400, 400, 1.0f,};

//英雄job到职业伤害加成属性映射
const proto_ff::AttrType arrJobToProfDmgAmplify[] = {
    proto_ff::EN_ATTR_MIN, proto_ff::EN_ATTR_PROF_A_DMG_AMPLIFY, proto_ff::EN_ATTR_PROF_B_DMG_AMPLIFY, proto_ff::EN_ATTR_PROF_C_DMG_AMPLIFY, proto_ff::EN_ATTR_PROF_D_DMG_AMPLIFY, proto_ff::EN_ATTR_PROF_E_DMG_AMPLIFY, proto_ff::EN_ATTR_PROF_F_DMG_AMPLIFY,
};

//英雄special属性转化对应关系
const proto_ff::AttrType arrHeroSpecialAttr[][2] = {
    {proto_ff::EN_ATTR_PHY_DMG, proto_ff::EN_ATTR_PHY_DMG_FACTOR}, {proto_ff::EN_ATTR_MGC_DMG, proto_ff::EN_ATTR_MGC_DMG_FACTOR}, {proto_ff::EN_ATTR_ARMOR, proto_ff::EN_ATTR_ARMOR_FACTOR}, {proto_ff::EN_ATTR_RESIST, proto_ff::EN_ATTR_RESIST_FACTOR},
    {proto_ff::EN_ATTR_MAX_HP, proto_ff::EN_ATTR_MAX_HP_FACTOR}, {proto_ff::EN_ATTR_MAX_MP, proto_ff::EN_ATTR_MAX_MP_FACTOR}, {proto_ff::EN_ATTR_CRI_RATE, proto_ff::EN_ATTR_CRI_RATE_FACTOR},
};

//英雄属性转化对应关系公式1
const proto_ff::AttrType arrHeroExtraAttr1[][4] = {
    {proto_ff::EN_ATTR_PHY_DMG, proto_ff::EN_ATTR_STR, proto_ff::EN_ATTR_PHY_DMG_FACTOR, proto_ff::EN_ATTR_STR_PHY_DMG_FACTOR}, {proto_ff::EN_ATTR_MGC_DMG, proto_ff::EN_ATTR_MAG, proto_ff::EN_ATTR_MGC_DMG_FACTOR, proto_ff::EN_ATTR_MAG_MGC_DMG_FACTOR},
    {proto_ff::EN_ATTR_MAX_HP, proto_ff::EN_ATTR_DEX, proto_ff::EN_ATTR_MAX_HP_FACTOR, proto_ff::EN_ATTR_VIT_MAX_HP_FACTOR}, {proto_ff::EN_ATTR_MAX_MP, proto_ff::EN_ATTR_SPR, proto_ff::EN_ATTR_MAX_MP_FACTOR, proto_ff::EN_ATTR_VIT_MAX_MP_FACTOR},
    {proto_ff::EN_ATTR_HP_RECOVERY, proto_ff::EN_ATTR_DEX, proto_ff::EN_ATTR_MIN, proto_ff::EN_ATTR_DEX_HP_RECOVERY_FACTOR}, {proto_ff::EN_ATTR_MP_RECOVERY, proto_ff::EN_ATTR_SPR, proto_ff::EN_ATTR_MIN, proto_ff::EN_ATTR_SPR_MP_RECOVERY_FACTOR},
};

//英雄属性转化对应关系公式2
const proto_ff::AttrType arrHeroExtraAttr2[][4] = {
    {proto_ff::EN_ATTR_ARMOR, proto_ff::EN_ATTR_DEX, proto_ff::EN_ATTR_ARMOR_FACTOR, proto_ff::EN_ATTR_DEX_ARMOR_FACTOR}, {proto_ff::EN_ATTR_RESIST, proto_ff::EN_ATTR_SPR, proto_ff::EN_ATTR_RESIST_FACTOR, proto_ff::EN_ATTR_SPR_RESIST_FACTOR},
    {proto_ff::EN_ATTR_CRI_RATE, proto_ff::EN_ATTR_LUCK, proto_ff::EN_ATTR_CRI_RATE_FACTOR, proto_ff::EN_ATTR_LUCK_CRI_RATE_FACTOR},
};

//英雄属性转化对应关系公式3
const proto_ff::AttrType arrHeroExtraAttr3[][2] = {{proto_ff::EN_ATTR_PHY_DMG, proto_ff::EN_ATTR_PHY_DMG_PERCENT}, {proto_ff::EN_ATTR_MGC_DMG, proto_ff::EN_ATTR_MGC_DMG_PERCENT},};

//伙伴资质属性
const proto_ff::AttrType arrPartnerAptitude[] = {proto_ff::EN_ATTR_STR, proto_ff::EN_ATTR_MAG, proto_ff::EN_ATTR_DEX, proto_ff::EN_ATTR_VIT, proto_ff::EN_ATTR_LUCK, proto_ff::EN_ATTR_SPR,};

//伙伴对英雄属性加成属性
const proto_ff::AttrType arrPartnerAttrToHero[] = {proto_ff::EN_ATTR_STR, proto_ff::EN_ATTR_MAG, proto_ff::EN_ATTR_DEX, proto_ff::EN_ATTR_SPR, proto_ff::EN_ATTR_VIT, proto_ff::EN_ATTR_LUCK,};

//伙伴资质系统对应常量
const proto_ff::enConstType arrPartnerAptitudeRatio[] = {proto_ff::EN_CONST_PARTNER_APTITUDE_RATIO_3, proto_ff::EN_CONST_PARTNER_APTITUDE_RATIO_4, proto_ff::EN_CONST_PARTNER_APTITUDE_RATIO_5, proto_ff::EN_CONST_PARTNER_APTITUDE_RATIO_6, proto_ff::EN_CONST_PARTNER_APTITUDE_RATIO_7,};

const proto_ff::AttrType arrHeroToPartnerAttr[] = {proto_ff::EN_ATTR_FHR, proto_ff::EN_ATTR_HRR, proto_ff::EN_ATTR_FGR, proto_ff::EN_ATTR_GR, proto_ff::EN_ATTR_WR, proto_ff::EN_ATTR_RRW, proto_ff::EN_ATTR_ABSW, proto_ff::EN_ATTR_FCR,};

inline int AddCE(RoleCEData* pCEData, enCEType eType, int64_t llCE)
{
    CHECK_NULL(0, pCEData);
    CHECK_EXPR(proto_ff::enCEType_IsValid(eType), -1, "invalid type{}", eType);
    CHECK_EXPR(eType >= 0 && eType < ARRAYSIZE(pCEData->ce_list), -1, "invalid type{}", eType);

    DEBUG_EXPR_CHECK_OP_2_PLUS(pCEData->ce_list[(int)eType], llCE);

    ADD_INT64(pCEData->ce_list[(int)eType], llCE);

    return 0;
}

template <typename T>
inline int AddAttrValueNoCheckRaw(CSAttr& attr, T& value)
{
    DEBUG_EXPR_CHECK_OP_2_PLUS(attr.value, value);

    ADD_INT64(attr.value, value);

    return 0;
}

template <typename T>
inline int AddAttrValue(CSAttr& attr, const T& value)
{
    if (IS_RAW_VALUE_ATTR(attr.type))
    {
        DEBUG_EXPR_CHECK_OP_2_PLUS(attr.value, value);

        ADD_INT64(attr.value, value);
    }
    else
    {
        DEBUG_EXPR_CHECK_OP_3_MUL_PLUS(value, ATTR_VALUE_MULTI, attr.value);

        ADD_INT64(attr.value, value * ATTR_VALUE_MULTI);
    }

    return 0;
}

inline int AddAttrValue(CSAttr& attr, const CSAttr& addAttr)
{
    DEBUG_EXPR_CHECK_OP_2_PLUS(attr.value, addAttr.value);

    ADD_INT64(attr.value, addAttr.value);

    return 0;
}

inline int SetAttr(CSAttr& attr, const CSAttr& value)
{
    attr = value;

    return 0;
}

template <typename T>
inline int SetAttrValueNoCheckRaw(CSAttr& attr, T value)
{
    SET_INT64(attr.value, value);

    return 0;
}

template <typename T>
inline int SetAttrValue(CSAttr& attr, T value)
{
    if (IS_RAW_VALUE_ATTR(attr.type))
    {
        SET_INT64(attr.value, value);
    }
    else
    {
        DEBUG_EXPR_CHECK_OP_2_MUL(value, ATTR_VALUE_MULTI);

        SET_INT64(attr.value, value * ATTR_VALUE_MULTI);
    }

#ifdef NF_DEBUG_MODE
    if (attr.type == EN_ATTR_MIN && attr.value != 0)
    {
        LOG_ERR(0, -1, "attr type error, TRACE_STACK:{}", TRACE_STACK());
    }
#endif

    return 0;
}

template <typename T>
inline int GetAttrValue(const CSAttr& attr, T& value)
{
    if (IS_RAW_VALUE_ATTR(attr.type))
    {
        value = attr.value;
    }
    else
    {
        value = (T)attr.value / (T)ATTR_VALUE_MULTI;
    }

#ifdef NF_DEBUG_MODE
    if (attr.type == EN_ATTR_MIN && attr.value != 0)
    {
        LOG_ERR(0, -1, "attr type error, TRACE_STACK:{}", TRACE_STACK());
    }
#endif

    return 0;
}

template <typename T>
inline int MultiAttrValue(CSAttr& attr, T value)
{
    DEBUG_EXPR_CHECK_OP_2_MUL(attr.value, value);

    SET_INT64(attr.value, attr.value*value);

    return 0;
}


template <typename T1, typename T2>
inline int AddAttrValueNoCheckRaw(T2& rOwner, int eType, T1 value)
{
    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        LOGSVR_DEBUG("attr zero");
        return -1;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return AddAttrValueNoCheckRaw<T1>(rOwner.attr_list[eType], value);
}

template <typename T1, typename T2>
inline int AddAttrValue(T2& rOwner, int eType, const T1 value)
{
    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        LOGSVR_DEBUG("attr zero");
        return -1;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return AddAttrValue<T1>(rOwner.attr_list[eType], value);
}

template <typename T1, typename T2>
inline int SetAttrValuenNoCheckRaw(T2& rOwner, int eType, T1 value)
{
    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        LOGSVR_DEBUG("attr zero");
        return -1;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return SetAttrValuenNoCheckRaw<T1>(rOwner.attr_list[eType], value);
}

template <typename T1>
inline int SetAttr(T1& rOwner, int eType, const CSAttr& value)
{
    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        LOGSVR_DEBUG("attr zero");
        return -1;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return SetAttr(rOwner.attr_list[eType], value);
}

template <typename T1, typename T2>
inline int SetAttrValue(T2& rOwner, int eType, T1 value)
{
    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        LOGSVR_DEBUG("attr zero");
        return -1;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return SetAttrValue<T1>(rOwner.attr_list[eType], value);
}

template <typename T1, typename T2>
inline int GetAttrValue(const T2& rOwner, int eType, T1& value)
{
    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        value = 0;

        return 0;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return GetAttrValue<T1>(rOwner.attr_list[eType], value);
}

template <typename T>
inline int AddAttrValue(T& rOwner, const CSAttr& attr)
{
    proto_ff::AttrType eType = (proto_ff::AttrType)attr.type;

    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        LOGSVR_DEBUG("attr zero");
        return -1;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return AddAttrValue(rOwner.attr_list[eType], attr);
}

template <typename T1, typename T2>
inline int GetAttrValueByType(const T2& rOwner, int eType, T1& value)
{
    int iIdx = -1;
    for (int i = 0; i < rOwner.attr_list_size && i < (int)ARRAYSIZE(rOwner.attr_list); ++i)
    {
        if (rOwner.attr_list[i].type == eType)
        {
            iIdx = 0;
            break;
        }
    }

    if (eType == proto_ff::EN_ATTR_MIN)
    {
        value = 0;

        return 0;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return GetAttrValue<T1>(rOwner.attr_list[iIdx], value);
}

template <typename T1, typename T2>
inline int MultiAttrValue(T2& rOwner, int eType, T1 value)
{
    CHECK_EXPR(eType >= 0 && eType < (int)ARRAYSIZE(rOwner.attr_list), -1, "attr array too small:{} attr type:{}  TRACE_STACK:{}", (int)ARRAYSIZE(rOwner.attr_list), eType, TRACE_STACK());

    if (eType == 0)
    {
        LOGSVR_DEBUG("attr zero");
        return -1;
    }

    CHECK_EXPR(IS_VALID_ATTR(eType), -1, "invalid attr type:{} TRACE_STACK:{}", eType, TRACE_STACK());

    return MultiAttrValue<T1>(rOwner.attr_list[eType], value);
}

template <typename T>
inline int SetExtraAttr(T& rOwner)
{
    int iRetCode = 0;

    for (int i = 0; i < (int)ARRAYSIZE(arrHeroExtraAttr1); ++i)
    {
        double lfValue0 = 0.0f;
        double lfValue1 = 0.0f;
        double lfValue2 = 0.0f;
        double lfValue3 = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr1[i][0], lfValue0);
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr1[i][1], lfValue1);
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr1[i][2], lfValue2);
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr1[i][3], lfValue3);

        double lfValue = lfValue0 * (1.0f + lfValue1 * lfValue3) * (1.0f + lfValue2);
        SET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr1[i][0], lfValue);

#ifdef NF_DEBUG_MODE
        {
            CExprCheck stCheck;
            stCheck.PushNum(lfValue0);
            stCheck.PushNum(1.0f);
            stCheck.PushNum(lfValue1);
            stCheck.PushNum(lfValue3);
            stCheck.PushOp(EN_EXPR_OP_MUL);
            stCheck.PushOp(EN_EXPR_OP_PLUS);
            stCheck.PushOp(EN_EXPR_OP_MUL);
            stCheck.PushNum(1.0f);
            stCheck.PushNum(lfValue2);
            stCheck.PushOp(EN_EXPR_OP_PLUS);
            stCheck.PushOp(EN_EXPR_OP_MUL);

            stCheck.CalcExprInt64();

            EXPR_CHECK_OVERFLOW(stCheck, lfValue);
        }
#endif
    }

    for (int i = 0; i < (int)ARRAYSIZE(arrHeroExtraAttr2); ++i)
    {
        double lfValue0 = 0.0f;
        double lfValue1 = 0.0f;
        double lfValue2 = 0.0f;
        double lfValue3 = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr2[i][0], lfValue0);
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr2[i][1], lfValue1);
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr2[i][2], lfValue2);
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr2[i][3], lfValue3);

        double lfValue = (lfValue0 + lfValue1 * lfValue3) * (1.0f + lfValue2);
        SET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr2[i][0], lfValue);

#ifdef NF_DEBUG_MODE
        {
            CExprCheck stCheck;
            stCheck.PushNum(lfValue0);
            stCheck.PushNum(lfValue1);
            stCheck.PushNum(lfValue3);
            stCheck.PushOp(EN_EXPR_OP_MUL);
            stCheck.PushOp(EN_EXPR_OP_PLUS);
            stCheck.PushNum(1.0f);
            stCheck.PushNum(lfValue2);
            stCheck.PushOp(EN_EXPR_OP_PLUS);
            stCheck.PushOp(EN_EXPR_OP_MUL);

            stCheck.CalcExprInt64();

            EXPR_CHECK_OVERFLOW(stCheck, lfValue);
        }
#endif
    }

    for (int i = 0; i < (int)ARRAYSIZE(arrHeroExtraAttr3); ++i)
    {
        double lfValue0 = 0.0f;
        double lfValue1 = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr3[i][0], lfValue0);
        GET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr3[i][1], lfValue1);

        double lfValue = (lfValue0) * (1.0f + lfValue1);
        SET_ATTR_VALUE_AND_CHECK_RET(rOwner, arrHeroExtraAttr3[i][0], lfValue);

#ifdef NF_DEBUG_MODE
        {
            CExprCheck stCheck;
            stCheck.PushNum(lfValue0);
            stCheck.PushNum(1.0f);
            stCheck.PushNum(lfValue1);
            stCheck.PushOp(EN_EXPR_OP_PLUS);
            stCheck.PushOp(EN_EXPR_OP_MUL);

            stCheck.CalcExprInt64();

            EXPR_CHECK_OVERFLOW(stCheck, lfValue);
        }
#endif
    }

    double lfCDRdu = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU, lfCDRdu);
    SET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU, MIN(lfCDRdu, MAX_CD_RDU));


    double lfCDRduPro = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU_PRO, lfCDRduPro);
    SET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU_PRO, MIN(lfCDRduPro, MAX_CD_RDU_PRO));

    double lfCDRduForceDodge = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU_FORCE_DODGE, lfCDRduForceDodge);
    SET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU_FORCE_DODGE, MIN(lfCDRduForceDodge, MAX_CD_RDU_FORCE_DODGE));

    double lfCDRduPartnerSP = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU_PARTNER_SP, lfCDRduPartnerSP);
    SET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_RDU_PARTNER_SP, MIN(lfCDRduPartnerSP, MAX_CD_RDU_PARTNER_SP));

    double lfCDRduDodge = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_DODGE, lfCDRduDodge);
    SET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_DODGE, MIN(lfCDRduDodge, MAX_CD_RDU_DODGE));

    double lfCDRduBuffSkill = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_BUFF_SKILL, lfCDRduBuffSkill);
    SET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_CD_BUFF_SKILL, MIN(lfCDRduBuffSkill, MAX_CD_RDU_BUFF_SKILL));

    double lfMoveSpd = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_MOV_SPD, lfMoveSpd);
    SET_ATTR_VALUE_AND_CHECK_RET(rOwner, proto_ff::EN_ATTR_MOV_SPD, MIN(lfMoveSpd, MAX_MOV_SPD_GAME));

    return 0;
}

inline double GetHeroSpecialFactor(const E_Actor& rActorDesc, int eType)
{
    double lfValue = 0.0f;
    switch (eType)
    {
        case proto_ff::EN_ATTR_PHY_DMG_FACTOR:
            lfValue = rActorDesc.specialist_attr_phy_dmg_factor;
            break;
        case proto_ff::EN_ATTR_MGC_DMG_FACTOR:
            lfValue = rActorDesc.specialist_attr_mgc_dmg_factor;
            break;
        case proto_ff::EN_ATTR_ARMOR_FACTOR:
            lfValue = rActorDesc.specialist_attr_armor_factor;
            break;
        case proto_ff::EN_ATTR_RESIST_FACTOR:
            lfValue = rActorDesc.specialist_attr_resist_factor;
            break;
        case proto_ff::EN_ATTR_MAX_HP_FACTOR:
            lfValue = rActorDesc.specialist_attr_max_hp_factor;
            break;
        case proto_ff::EN_ATTR_MAX_MP_FACTOR:
            lfValue = rActorDesc.specialist_attr_max_mp_factor;
            break;
        case proto_ff::EN_ATTR_CRI_RATE_FACTOR:
            lfValue = rActorDesc.specialist_attr_cri_rate_factor;
            break;
        default:
            break;
    }

    return lfValue;
}
